/**
 * <p>文件名:		FileHelper.java</p>
 * <p>版权:		CopyrightTag</p>
 * <p>公司:		CompanyTag</p>
 * @author		周华彬(zhouhuabin@ctfo.com)
 */

package com.caits.lbs.framework.utils;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;

import org.apache.logging.log4j.Logger;

import com.caits.lbs.framework.log.CommonLogFactory;

/**
 * <p>
 * FileHelper
 * </p>
 * <p>
 * 文件助手，用来获取文件相关的支持，与配置池项配合，实现3级的配置体系
 * </p>
 * 
 * @author 周华彬(zhouhuabin@ctfo.com)
 * @version 0.0.0
 *          <table style="border:1px solid gray;">
 *          <tr>
 *          <th width="100px">版本号</th>
 *          <th width="100px">动作</th>
 *          <th * width="100px">修改人</th>
 *          <th width="100px">修改时间</th>
 *          </tr>
 *          <!-- 以 Table 方式书写修改历史 -->
 *          <tr>
 *          <td>0.0.0</td>
 *          <td>创建类</td>
 *          <td>周华彬</td>
 *          <td>2008-7-14 下午02:24:05</td>
 *          </tr>
 *          <tr>
 *          <td>XXX</td>
 *          <td>XXX</td>
 *          <td>XXX</td>
 *          <td>XXX</td>
 *          </tr>
 *          </table>
 */
public class FileUtils {

	/** 日志记录器 */
	protected static Logger log = CommonLogFactory.getLog();
	/** 系统默认字符集，-Dfile.encoding=xxx */
	public static final String CONST_DEFAULT_CHARSET = Charset.defaultCharset().name();
	public static final String CONST_GBK_CHARSET = Charset.forName("GBK").name();
	public static final String CONST_UTF8_CHARSET = Charset.forName("UTF-8").name();
	public static final String CONST_ISO8859_CHARSET = Charset.forName("iso-8859-1").name();
	
	/** 缓冲区大小定义 */
	public static final int CONST_BUFFERSIZE_10K=1024*10;
	public static final int CONST_BUFFERSIZE_100K=1024*100;
	public static final int CONST_BUFFERSIZE_1M=1024*1024;
	public static final int CONST_BUFFERSIZE_5M=1024*1024*5;
	public static final int CONST_BUFFERSIZE_10M=1024*1024*10;
	/** 当前用户工作路径user.dir，在开发工具下运行为当前项目路径 */
	private static String userDir = ".";

	/**
	 * 初始化文件
	 */
	public static void init() {
	}

	/**
	 * 文件转换为字符串,只能读取10M以下的文件
	 * 
	 * @param file
	 * @param charSet
	 *            文件的字符集
	 * @return
	 */
	public static String fileToString(File file, String charSet) {
		StringBuilder sb = new StringBuilder();
		try {
			InputStream is = new FileInputStream(file);
			BufferedReader br = new BufferedReader(new InputStreamReader(is, charSet),CONST_BUFFERSIZE_10M);
			String line = "";
			while ((line = br.readLine()) != null) {
				sb.append(line).append("\r\n");
			}
			br.close();
			is.close();
		} catch (Exception e) {
			e.printStackTrace();
			sb.append("error");
			return null;
		}
		return sb.toString();
	}

	/**
	 * 将资源路径下载到输出流中
	 * 
	 * @param filePath
	 *            要下载的资源文件,可以是http,classpaht,本地路径
	 * @param output
	 *            要输出的流对象
	 * @param bufSize	缓冲区大小,bytes
	 * @return
	 */
	public static boolean filePath2Stream(String filePath, OutputStream output,int bufSize) {
		InputStream is = null;
		try {
			is = createInputStream(filePath);
			byte[] bitData = new byte[bufSize];
			try {
				int len, totalLen = 0;
				while ((len = is.read(bitData)) > 0) {
					output.write(bitData, 0, len);
					totalLen += len;
				}
				log.info("下载filePath="+filePath+"成功,大小:" + totalLen);
			} catch (IOException e) {
				log.error("读取数据文件失败！msg=" + e.getLocalizedMessage());
			}

		} catch (Exception e) {
			log.error("下载远程URL出错! url=" + filePath + " " + e.getMessage());
			return false;
		} finally {
			try {
				if (is != null)
					is.close();
			} catch (IOException e) {
				log.error("关闭对象时出错! cityCode=" + filePath + e.getMessage());
				return false;
			}
		}
		return true;
	}

	/**
	 * 将url保存为本地文件
	 * 
	 * @param urlName
	 *            url地址
	 * @param fileName
	 *            本地文件名
	 * @return
	 */
	public static boolean url2file(String urlName, String fileName) {
		try {
			URL url = new URL(urlName);
			java.io.BufferedInputStream bis = new BufferedInputStream(url.openStream());
			byte[] bytes = new byte[1024 * 1024];
			OutputStream bos = new FileOutputStream(new File(fileName));
			int len;
			while ((len = bis.read(bytes)) > 0) {
				bos.write(bytes, 0, len);
			}
			bis.close();
			bos.flush();
			bos.close();

		} catch (Exception e) {
			log.error("保存远程url到本地文件出错,msg=" + e.getLocalizedMessage(), e);
			return false;
		}
		return true;
	}

	/**
	 * 获取url的内容，返回字符串
	 * 
	 * @param method
	 *            get或者post
	 * @param charset
	 * @param url
	 * @param body
	 * @param header
	 * @return 远程url返回的内容
	 */
	public static String url2string(String method, String charset, String url, String body, String header) {
		StringBuffer buffer = new StringBuffer();
		try {
			URL urlObj = new URL(url);
			URLConnection conn = urlObj.openConnection();
			conn.setConnectTimeout(5000);
			conn.setReadTimeout(5000);
			// 接收返回信息
			BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), charset));
			int ch;
			while ((ch = reader.read()) != -1) {
				buffer.append((char) ch);
			}
		} catch (MalformedURLException e) {
			log.error("保存远程url到本地文件出错,msg=" + e.getLocalizedMessage(), e);
		} catch (IOException e) {
			log.error("保存远程url到本地文件出错,msg=" + e.getLocalizedMessage(), e);
		} catch (Exception e) {
			log.error("保存远程url到本地文件出错,msg=" + e.getLocalizedMessage(), e);

		}
		return buffer.toString();
	}

	/**
	 * 判断文件对象是否有效
	 * 
	 * @param file
	 * @return
	 */
	public static boolean validFile(File file) {
		if (file == null)
			return false;
		return file.exists();
	}

	/**
	 * 根据文件对象创建缓冲读
	 * 
	 * @param file
	 * @return 字段 userDir 获取函数
	 * 
	 * @return the userDir : String
	 */
	public static String getUserDir() {
		return userDir;
	}

	/**
	 * 字段 userDir 设置函数 : String
	 * 
	 * @param userDir
	 *            the userDir to set
	 */
	public static void setUserDir(String userDir) {
		FileUtils.userDir = userDir;
	}

	/**
	 * 创建文件输入流
	 * 
	 * @param filepath
	 *            文件路径
	 * @return
	 * @throws IOException
	 */
	public static InputStream createInputStream(String filepath) throws IOException {
		if (filepath.startsWith("classpath:")) {
			String clspath = filepath.substring(10);
			InputStream is = FileUtils.class.getClassLoader().getResourceAsStream(clspath);
			if (is == null)
				throw new IOException(filepath + " not found.");
			return is;
		} else if (filepath.startsWith("http:") || filepath.startsWith("file:") || filepath.startsWith("ftp:")) {
			URL url = null;
			HttpURLConnection httpUrl = null;
			try {
				url = new URL(filepath);
				httpUrl = (HttpURLConnection) url.openConnection();
				httpUrl.setConnectTimeout(5000);
				httpUrl.setReadTimeout(5000);
				return httpUrl.getInputStream();
			} catch (Exception e) {
				log.error("读取远程文件出错,url=" + filepath + ",msg=" + e.getLocalizedMessage());
				return null;
			}
		} else {
			InputStream is = new FileInputStream(filepath);
			return is;
		}
	}
	/**
	 * 根据路径创建输出流 
	 * @param filepath	目前支持http和本地路径，不支持classpath
	 * @return
	 * @throws IOException
	 */
	public static OutputStream createOutputStream(String filepath, boolean append) throws IOException {
		if (filepath.startsWith("classpath:")) {
			throw new IOException(filepath + " classpath could not write.");
		} else if (filepath.startsWith("http:") || filepath.startsWith("file:") || filepath.startsWith("ftp:")) {
			URL url = null;
			HttpURLConnection httpUrl = null;
			try {
				url = new URL(filepath);
				httpUrl = (HttpURLConnection) url.openConnection();
				httpUrl.setConnectTimeout(5000);
				httpUrl.setReadTimeout(5000);
				return httpUrl.getOutputStream();
			} catch (Exception e) {
				log.error("读取远程文件出错,url=" + filepath + ",msg=" + e.getLocalizedMessage());
				return null;
			}
		} else {
			OutputStream os = new FileOutputStream(filepath,append);
			return os;
		}
	}

	/**
	 * 根据文件路径创建读入流
	 * 
	 * @param file
	 *            本地文件路况
	 * @param charSet
	 *            字符集
	 * @return
	 */
	public static BufferedReader createBufferReaderByFile(String filepath, String charSet, int bufferSize) {
		BufferedReader reader = null;
		try {
			InputStream stream = createInputStream(filepath);
			reader = new BufferedReader(new InputStreamReader(stream, charSet), bufferSize);
		} catch (FileNotFoundException e) {
			log.error("获取文件流出错,msg=" + filepath, e);
		} catch (IOException e) {
			log.error("获取文件流出错,msg=" + filepath, e);
		}
		return reader;
	}

	/**
	 * 根据路径读取文件流
	 * 
	 * @param dataPath
	 *            文件路况
	 * @param timeout
	 *            http时，超时时间毫秒值
	 * @param charSet
	 *            文件的字符集
	 * @return BufferedReader 缓冲读
	 */
	public static BufferedReader createBufferedReader(String dataPath, int timeout, String charSet, int bufferSize) {
		BufferedReader bufferedReader = null;
		InputStream inputStream = null;
		if (dataPath.toLowerCase().startsWith("http:")) {
			URL url = null;
			HttpURLConnection httpUrl = null;
			try {
				url = new URL(dataPath);
				httpUrl = (HttpURLConnection) url.openConnection();
				httpUrl.setConnectTimeout(timeout);
				httpUrl.setReadTimeout(timeout);
				inputStream = httpUrl.getInputStream();
				bufferedReader = new BufferedReader(new InputStreamReader(inputStream, charSet), bufferSize);
			} catch (Exception e) {
				log.error("读取远程文件出错,url=" + dataPath + ",msg=" + e.getLocalizedMessage());
				return null;
			}
		} else {
			try {
				bufferedReader = createBufferReaderByFile(dataPath, charSet, bufferSize);
			} catch (Exception e) {
				log.error("读取本地文件出错,path=" + dataPath, e);
				return null;
			}
		}
		return bufferedReader;
	}

	/**
	 * 创建缓冲流写入
	 * 
	 * @param fileName
	 *            本地文件路径名，自动创建目录
	 * @param charset
	 *            写入字符集
	 * @param append
	 *            如果文件存在，是否追加
	 * @return 写入器对象
	 * @throws IOException
	 */
	public static BufferedWriter createBufferedWriterByFile(String fileName, String charSet, boolean append, int bufferSize) throws IOException {
		FileUtils.makePathByFileName(fileName);
		File file = new File(fileName);
		BufferedWriter bw = null;
		try {
			FileOutputStream fo = new FileOutputStream(file, append);
			bw = new BufferedWriter(new OutputStreamWriter(fo, charSet), bufferSize);
		} catch (IOException e) {
			log.error("创建写入缓冲流失败,msg=" + e.getLocalizedMessage(), e);
			throw e;
		}
		return bw;
	}

	/**
	 * 创建文件写入器
	 * 
	 * @param fileName
	 *            本地文件路径名，自动创建目录
	 * @param append
	 *            如果文件存在，是否追加
	 * @return 写入器对象
	 * @throws IOException
	 */
	public static FileWriter createFileWriter(String fileName, boolean append) throws IOException {
		FileUtils.makePathByFileName(fileName);
		File file = new File(fileName);
		// FileOutputStream fo = new FileOutputStream(file, append);
		FileWriter fw = null;
		try {
			fw = new FileWriter(file, append);
		} catch (IOException e) {
			log.error("创建写入流失败,msg=" + e.getLocalizedMessage(), e);
			throw e;
		}
		return fw;
	}

	/**
	 * 根据相对路径获得文件绝对路径
	 * 
	 * @param filepath
	 *            原始路径可以是classpath,http,file的前缀，也可以无前缀
	 * @return 实际绝对路径
	 */
	public static String getRealPathByRelative(ClassLoader loader, String filepath) {
		if (filepath == null)
			return null;
		String resultPath = null;
		try {
			if (filepath.startsWith("classpath:")) {
				String clspath = filepath.substring(10);
				URL url = loader.getResource(".");
				if (url == null)
					url = FileUtils.class.getClassLoader().getResource(".");
				if (url == null)
					url = new URL(".");
				resultPath = url.getPath() + File.separator + clspath;
				return resultPath;
			} else if (filepath.startsWith("http:") || filepath.startsWith("file:") || filepath.startsWith("ftp:")) {
				resultPath = new URL(filepath).getPath();
				return resultPath;
			} else {
				File directory = new File(filepath);
				resultPath = directory.getAbsolutePath();
			}
		} catch (Exception e) {
			log.error("获取文件路径出错,filepath=" + filepath + ",msg=" + e.getLocalizedMessage(), e);
		}
		return resultPath;
	}

	/**
	 * 写文件时根据文件名创建上级目录结构
	 * 
	 * @param fileName
	 * @return
	 */
	public static String makePathByFileName(String fileName) {
		File directory = new File(fileName);
		File newPath = null;
		String currentDir = null;
		try {
			newPath = new File(directory.getParent());
			newPath.mkdirs();
		} catch (Exception exp) {
			log.error("获取文件路径出错,path=" + fileName, exp);
		}
		if (newPath != null)
			currentDir = newPath.getPath() + File.separator;
		else
			currentDir = "\\";
		return currentDir;
	}

	/**
	 * 将输入流转换为字节数组
	 * 
	 * @param inStream
	 * @return
	 * @throws IOException
	 */
	public static byte[] inputStreamToByte(InputStream inStream) throws IOException {
		ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
		byte[] buff = new byte[CONST_BUFFERSIZE_10K];
		int rc = 0;
		while ((rc = inStream.read(buff, 0, buff.length)) > 0) {
			swapStream.write(buff, 0, rc);
		}
		return swapStream.toByteArray();
	}

	/**
	 * 判断远程url文件是否存在,文件内容可以很大,不会下载到本地
	 * 
	 * @param urlName
	 *            url文件路径,
	 * @param timeout
	 *            http时，超时时间毫秒值
	 * @return true-存在,false-不存在
	 */
	public static boolean validUrlFile(String urlName,int timeOut) {
		try {
			// 设置此类是否应该自动执行 HTTP 重定向（响应代码为 3xx 的请求）。
			HttpURLConnection.setFollowRedirects(false);
			// 到 URL 所引用的远程对象的连接
			HttpURLConnection con = (HttpURLConnection) new URL(urlName).openConnection();
			con.setConnectTimeout(timeOut);
			con.setReadTimeout(timeOut);
			/*
			 * 设置 URL 请求的方法， GET POST HEAD OPTIONS PUT DELETE TRACE
			 * 以上方法之一是合法的，具体取决于协议的限制。
			 */
			con.setRequestMethod("HEAD");
			// 从 HTTP 响应消息获取状态码
			return (con.getResponseCode() == HttpURLConnection.HTTP_OK);
		} catch (Exception e) {
			log.error("检验url文件路径出错,path=" + urlName, e);
			return false;
		}
	}

	/**
	 * 文件转换为字符串
	 * 
	 * @param file
	 * @param charSet
	 *            文件的字符集
	 * @return
	 * @throws Exception
	 */

	public static void main(String[] args) throws Exception {
		String ret = getRealPathByRelative(ClassLoader.getSystemClassLoader(), "classpath:bus-data/busline.csv");
		String content = ret + "";
		System.out.println("读取后的内容是:" + content + ",gbk=" + CONST_GBK_CHARSET + ",utf8=" + CONST_UTF8_CHARSET);
		System.out.println("获取实际路径1是:" + getRealPathByRelative(Logger.class.getClassLoader(), "."));
	}
}
